# 导入所需库
import urllib.request  # 用于发送HTTP请求
import re  # 正则表达式处理
from lxml import etree  # HTML/XML解析
import time  # 时间相关操作
import random  # 生成随机数
import json  # JSON数据处理


def clean_chinese(text):
    """清洗中文字符和常见标点
    参数:
        text: 需要清洗的文本
    返回:
        只包含中文和常见标点的文本
    """
    # 使用正则表达式保留中文字符和常见中文标点
    return re.sub(r'[^\u4e00-\u9fa5，。！？【】（）%、]', '', str(text)).strip()


def clear_file():
    """初始化数据文件
    功能:
        清空Data.txt和medical.json文件内容
    """
    # 清空文本数据文件
    with open('Data.txt', 'w', encoding='utf-8') as f:
        pass  # 空操作，仅用于清空文件

    # 初始化JSON文件
    with open('medical.json', 'w', encoding='utf-8') as f:
        pass  # 创建空文件


def get_html():
    """主爬取函数
    功能:
        1. 遍历指定页码范围
        2. 发送HTTP请求获取页面内容
        3. 解析并提取所需数据
        4. 保存数据到文件
    """
    # 基础URL模板
    base_url = 'https://www.zhys.com/shicai/{}.html'

    # 请求头配置
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
                      'AppleWebKit/537.36 (KHTML, like Gecko) '
                      'Chrome/91.0.4472.124 Safari/537.36',
        'Referer': 'https://www.zhys.com/'  # 添加来源页防止反爬
    }

    # 爬虫参数设置
    max_retries = 3  # 最大重试次数
    request_delay = 1  # 基础请求延迟(秒)
    collected_data = []  # 数据存储列表(当前未使用)

    # 主循环：遍历0-9页(实际处理1-10页)
    for page in range(0, 10):
        page = page + 1  # 页码从1开始
        url = base_url.format(page)  # 格式化URL
        retry_count = 0  # 当前重试次数
        success = False  # 成功标志

        print(f"\n正在处理数据{page}")  # 打印当前处理页码

        # 重试循环
        while retry_count < max_retries and not success:
            try:
                # 随机延迟(1-1.5秒)避免被封
                time.sleep(request_delay + random.uniform(0, 0.5))

                # 创建请求对象
                req = urllib.request.Request(url=url, headers=headers)

                # 发送请求(15秒超时)
                with urllib.request.urlopen(req, timeout=15) as res:
                    # 检查HTTP状态码
                    if res.status != 200:
                        raise Exception(f"HTTP状态码异常: {res.status}")

                    # 读取并解码HTML内容
                    html = res.read().decode('utf-8')

                    # 使用lxml解析HTML
                    selector = etree.HTML(html)

                    # 使用XPath提取数据
                    # 提取主标题
                    h1 = selector.xpath('//h1[@class="detail-title"]/span[@class="d-t"]/text()')
                    # 提取别名(改进的contains语法)
                    h2 = selector.xpath('//span[contains(., "别名：")]/text()')
                    # 提取功效
                    h3 = selector.xpath('//div[contains(@class, "gongxiao")]/a/text()')
                    # 提取适宜人群(包含嵌套文本)
                    h4 = selector.xpath('//li[contains(@class,"c3")]/p//text()')
                    # 提取禁忌人群
                    h5 = selector.xpath('//li[contains(@class,"c4")]/p//text()')

                    # 数据清洗与异常处理
                    # 处理主标题(添加长度检查)
                    title1 = clean_chinese(h1[0].split('的简介')[0]) if h1 and len(h1) > 0 else "未知名称"
                    # 处理别名
                    title2 = clean_chinese(h2[0].split('：')[-1]) if h2 and len(h2) > 0 else "无别名"
                    # 处理功效列表
                    title3 = [clean_chinese(item) for item in h3] if h3 else []
                    # 处理适宜人群文本
                    title4 = clean_chinese("".join(h4)) if h4 else "无信息"
                    # 处理禁忌人群文本
                    title5 = clean_chinese("".join(h5)) if h5 else "无信息"

                    # 打印提取结果
                    print(f"名称: {title1}")
                    print(f"别名: {title2}")
                    print(f"功效: {'、'.join(title3)}")
                    print(f"适宜人群: {title4}")
                    print(f"禁忌人群: {title5}")

                    # 构建数据结构
                    data_entry = {
                        "id": page,  # 当前页码
                        "name": title1,  # 名称
                        "alias": title2,  # 别名
                        "effects": title3,  # 功效列表
                        "suitable_group": title4,  # 适宜人群
                        "taboo_group": title5,  # 禁忌人群
                        "source_url": url  # 来源URL
                    }

                    # 转换为JSON字符串(保留中文)
                    data = json.dumps(data_entry, ensure_ascii=False)

                    # 写入文本文件(追加模式)
                    with open('Data.txt', 'a', encoding='utf-8') as f:
                        f.write(
                            f"页码：{page}\n"
                            f"名称：{title1}\n"
                            f"别名：{title2}\n"
                            f"功效：{'、'.join(title3)}\n"
                            f"适宜人群：{title4}\n"
                            f"禁忌人群：{title5}\n\n"
                        )

                    # 写入JSON文件(追加模式)
                    with open('medical.json', 'a', encoding='utf-8') as f:
                        f.writelines(data)  # 写入数据
                        f.write('\n')  # 添加换行分隔

                    f.close()  # 显式关闭文件
                    success = True  # 标记成功
                    print(f"数据 {page} 采集成功")  # 打印成功信息

            except Exception as e:
                # 异常处理
                retry_count += 1  # 增加重试计数
                print(f"第 {page} 页第 {retry_count} 次尝试失败: {str(e)}")

                # 达到最大重试次数
                if retry_count == max_retries:
                    print(f"跳过第 {page} 页")

                    # 记录失败信息
                    with open('Data.txt', 'a', encoding='utf-8') as f:
                        f.write(f"页码：{page} 采集失败\n\n")

                    break  # 跳出重试循环


if __name__ == '__main__':
    """程序主入口"""
    try:
        # 初始化文件
        clear_file()

        # 记录开始时间
        start_time = time.time()

        # 执行主爬取函数
        get_html()

        # 记录结束时间
        end_time = time.time()

        # 打印总耗时
        print(f"\n数据采集完成！总耗时: {end_time - start_time:.2f}秒")

        # 统计采集结果(注: 当前medical.json是逐行写入，需调整格式才能直接load)
        # 正确做法应该是先收集所有数据再统一写入，或使用JSON数组格式
        # 以下代码需要medical.json是标准JSON格式才能工作
        try:
            with open('medical.json', 'r', encoding='utf-8') as f:
                data = json.load(f)
                print(f"成功采集 {len(data)} 条数据")
        except:
            print("无法统计JSON数据，可能格式不正确")

    except KeyboardInterrupt:
        # 处理用户中断(Ctrl+C)
        print("\n用户中断程序执行")

    except Exception as e:
        # 处理其他异常
        print(f"程序运行出错: {str(e)}")